package com.shm.leetcode;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.TreeMap;

/**
 * @author: shm
 * @dateTime: 2020/12/8 10:07
 * @description: 842. 将数组拆分成斐波那契序列
 * 给定一个数字字符串 S，比如 S = "123456579"，我们可以将它分成斐波那契式的序列 [123, 456, 579]。
 *
 * 形式上，斐波那契式序列是一个非负整数列表 F，且满足：
 *
 * 0 <= F[i] <= 2^31 - 1，（也就是说，每个整数都符合 32 位有符号整数类型）；
 * F.length >= 3；
 * 对于所有的0 <= i < F.length - 2，都有 F[i] + F[i+1] = F[i+2] 成立。
 * 另外，请注意，将字符串拆分成小块时，每个块的数字一定不要以零开头，除非这个块是数字 0 本身。
 *
 * 返回从 S 拆分出来的任意一组斐波那契式的序列块，如果不能拆分则返回 []。
 *
 *
 *
 * 示例 1：
 *
 * 输入："123456579"
 * 输出：[123,456,579]
 * 示例 2：
 *
 * 输入: "11235813"
 * 输出: [1,1,2,3,5,8,13]
 * 示例 3：
 *
 * 输入: "112358130"
 * 输出: []
 * 解释: 这项任务无法完成。
 * 示例 4：
 *
 * 输入："0123"
 * 输出：[]
 * 解释：每个块的数字不能以零开头，因此 "01"，"2"，"3" 不是有效答案。
 * 示例 5：
 *
 * 输入: "1101111"
 * 输出: [110, 1, 111]
 * 解释: 输出 [11,0,11,11] 也同样被接受。
 *
 *
 * 提示：
 *
 * 1 <= S.length <= 200
 * 字符串 S 中只含有数字。
 */
public class SplitIntoFibonacci {
    /**
     * 方法一：回溯 + 剪枝
     * 将给定的字符串拆分成斐波那契式序列，可以通过回溯的方法实现。
     *
     * 使用列表存储拆分出的数，回溯过程中维护该列表的元素，列表初始为空。遍历字符串的所有可能的前缀，作为当前被拆分出的数，然后对剩余部分继续拆分，直到整个字符串拆分完毕。
     *
     * 根据斐波那契式序列的要求，从第 33 个数开始，每个数都等于前 22 个数的和，因此从第 33 个数开始，需要判断拆分出的数是否等于前 22 个数的和，只有满足要求时才进行拆分，否则不进行拆分。
     *
     * 回溯过程中，还有三处可以进行剪枝操作。
     *
     * 拆分出的数如果不是 00，则不能以 00 开头，因此如果字符串剩下的部分以 00 开头，就不需要考虑拆分出长度大于 11 的数，因为长度大于 11 的数以 00 开头是不符合要求的，不可能继续拆分得到斐波那契式序列；
     *
     * 拆分出的数必须符合 3232 位有符号整数类型，即每个数必须在 [0,2^{31}-1][0,2
     * 31
     *  −1] 的范围内，如果拆分出的数大于 2^{31}-12
     * 31
     *  −1，则不符合要求，长度更大的数的数值也一定更大，一定也大于 2^{31}-12
     * 31
     *  −1，因此不可能继续拆分得到斐波那契式序列；
     *
     * 如果列表中至少有 22 个数，并且拆分出的数已经大于最后 22 个数的和，就不需要继续尝试拆分了。
     *
     * 当整个字符串拆分完毕时，如果列表中至少有 33 个数，则得到一个符合要求的斐波那契式序列，返回列表。如果没有找到符合要求的斐波那契式序列，则返回空列表。
     *
     * 实现方面，回溯需要带返回值，表示是否存在符合要求的斐波那契式序列。
     *
     * 复杂度分析
     *
     * 时间复杂度：O(n \log^2 C)O(nlog
     * 2
     *  C)，其中 nn 是字符串的长度，CC 是题目规定的整数范围 2^{31}-12
     * 31
     *  −1。在回溯的过程中，实际上真正进行「回溯」的只有前 22 个数，而从第 33 个数开始，整个斐波那契数列是可以被唯一确定的，整个回溯过程只起到验证（而不是枚举）的作用。对于前 22 个数，它们的位数不能超过 \lfloor \log_{10} C \rfloor⌊log
     * 10
     * ​
     *  C⌋，那么枚举的空间为 O(\log^2 C)O(log
     * 2
     *  C)；对于后面的所有数，回溯的过程是没有「分支」的，因此时间复杂度为 O(n)O(n)，相乘即可得到总时间复杂度 O(n \log^2 C)O(nlog
     * 2
     *  C)。
     *
     * 空间复杂度：O(n)O(n)，其中 nn 是字符串的长度。除了返回值以外，空间复杂度主要取决于回溯过程中的递归调用层数，最大为 nn。
     * 作者：LeetCode-Solution
     * 链接：https://leetcode-cn.com/problems/split-array-into-fibonacci-sequence/solution/jiang-shu-zu-chai-fen-cheng-fei-bo-na-qi-ts6c/
     *
     * @param S
     * @return
     */
    public List<Integer> splitIntoFibonacci(String S) {
        List<Integer> list = new ArrayList<>();
        backTrace(list,S,S.length(),0,0,0);
        return list;
    }

    public boolean backTrace(List<Integer> list,String s,int length,int index,int sum,int pre){
        if (index==length){
            return list.size()>=3;
        }
        long currLong = 0;
        for (int i = index; i < length; i++) {
            if (i>index&&s.charAt(index)=='0'){
                break;
            }
            currLong=currLong*10+s.charAt(i)-'0';
            if (currLong>Integer.MAX_VALUE){
                break;
            }
            int curr = (int)currLong;
            if (list.size()>=2){
                if (curr<sum){
                    continue;
                }else if (curr>sum){
                    break;
                }
            }
            list.add(curr);
            if (backTrace(list,s,length,i+1,curr+pre,curr)){
                return true;
            }else {
                list.remove(list.size()-1);
            }
        }
        return false;
    }

    public static void main(String[] args) {
        new TreeMap();
        new HashMap();
    }
}
